function UndoManager(textModel, selection){
    this.$initEventable();
    this.m_textModel = textModel;
    this.m_selection = selection;
    this.$on(this.m_textModel, 'textchanged', this._onTextChange);
    this.m_stack = [];
    this.m_index = 0;
    this.m_changes = [];
    this.m_prevent = false;
}

UndoManager.prototype = {
    undo: function (){
        if (this.m_index == this.m_stack.length) {
            this.save();
        }
        this.m_prevent = true;
        if (this.m_index == 0) return;
        var index = this.m_index - 1;
        var changes = this.m_stack[index];
        var k = changes.length;
        while (k --) {
            var change = changes[k];
            this.m_textModel.replaceText(
                change.offset,
                change.newText.length,
                change.oldText);
        }
        this.m_index = index;
        this.m_prevent = false;
        var prev = this.m_stack[index - 1];
        if (prev) {
            this.m_selection.setFocusOffset(prev.focusOffset);
            this.m_selection.setAnchorOffset(prev.anchorOffset);
        }
    },
    redo: function (){
        
    },
    save: function (){
        if (this.m_changes.length) {
            this.m_index = this.m_stack.push(this.m_changes);
            this.m_changes.focusOffset = this.m_selection.focusOffset();
            this.m_changes.anchorOffset = this.m_selection.anchorOffset();
            this.m_changes = [];
        }
    },
    _onTextChange: function (evt){
        if (this.m_prevent) return;
        if (this.m_index < this.m_stack.length) {
            this.m_stack.length = this.m_index;
        }
        this.m_changes.push(evt);
    }
};

Utils.extend(UndoManager, Eventable);